Introduction

In recent decades, Philadelphia has experienced substantial changes in population dynamics, economic fluctuations, and redevelopment pressures. Among these changes, housing values have become a key indicator of the socio-economic conditions within the city’s neighborhoods. Rising housing values typically reflect a higher presence of wealthier residents or a neighborhood undergoing gentrification, while declining housing values often signal increased economic pressure or decline. Therefore, accurately forecasting the median housing values across Philadelphia is crucial for urban planners and policymakers, helping them to proactively address potential risks such as displacement and disinvestment. Supporting Philadelphia’s long-term vision of fostering vibrant, diverse, and resilient communities is also essential.

To better understand the potential factors influencing housing values in Philadelphia, we identify several key variables: educational attainment, vacancy rates, the proportion of single-family homes, and poverty levels. These factors are closely related to housing market trends and provide insights into the neighborhood’s socio-economic status.

Educational attainment is closely linked to the socio-economic characteristics of neighborhoods. Individuals with higher educational attainment typically earn higher incomes and contribute more to local economies. A higher concentration of individuals with advanced educational backgrounds tends to increase the demand for well-designed housing in affluent neighborhoods. With a consistent supply, housing prices and values are likely to rise. Similarly, a high poverty rate also indicates a declining community. Since many residents cannot afford luxury housing, the housing values in those neighborhoods are likely to be Lower.

High vacancy rates often correlate with declining neighborhoods and reduced median house values. Research shows that vacant properties affect multiple facets of community life, such as housing and neighborhood vitality, crime prevention efforts, and the well-being of commercial districts. As a result, areas with numerous vacant housing units typically see diminished median housing values.

The proportion of single-family homes in an area influences housing values. These homes are generally private and comfortable, making them more desirable in many U.S. housing markets. However, they are relatively common in suburban neighborhoods and may suffer from low accessibility and limited infrastructure, which can negatively affect their property values as well.

In this study, we utilize ordinary least squares (OLS) regression to analyze the relationship between these socioeconomic factors and median house values in Philadelphia. By examining these relationships, we aim to identify critical predictors of median housing values throughout Philadelphia and offer insights for decision-makers and community initiatives.

Methods

Data Cleaning

To predict median house values in Philadelphia, we obtained the original dataset from the United States Census data. The dataset represents census block groups from the year 2000 and initially contained 1,816 observations. The key variables included:

  • POLY_ID – Census Block Group ID
  • MEDHVAL – Median value of all owner-occupied housing units
  • PCBACHMORE – Proportion of residents in the block group with at least a bachelor’s degree
  • PCTVACANT – Proportion of housing units that are vacant
  • PCTSINGLES – Percentage of housing units that are detached single-family houses
  • NBELPOV100 – Number of households with incomes below 100% of the poverty level
  • MEDHHINC – Median household income

To refine the dataset for modeling purposes, we applied the following filtering criteria:

  1. Retained block groups with a population greater than 40
  2. Included only block groups that contained housing units
  3. Excluded records where the median house value was below $10,000

Additionally, we removed a specific block group in North Philadelphia that exhibited inconsistencies, with an unusually high median house value (over $800,000) despite a very low median household income (less than $8,000).

After data cleaning, the final dataset contained 1,720 observations.

Exploratory Data Anaylsis

Summary Statistics

We will first examine the summary statistics of key variables in the dataset, including the dependent variables MEDHVAL (Median House Value), and predictors NBELPOV100 (Households Living in Poverty), PCTBACHMOR (% of Individuals with Bachelor’s Degrees or Higher), PCTVACANT(% of Vacant Houses), PCTSINGLES( % of Single House Units).

We will examine the mean and standard deviation (SD) of key variables in the dataset.

The mean (\(\bar{X}\)) represents the average value of a variable and is calculated as:

\[ \bar{X} = \frac{1}{n} \sum_{i=1}^{n} X_i \]

where:
- \(X_i\) represents each individual observation
- \(n\) is the total number of observations

The mean gives us a single representative value of the dataset, which helps in understanding the typical value for a given variable.

To measure variability, we use the standard deviation (SD), which quantifies how much the values in a dataset deviate from the mean. The formula for the sample standard deviation (\(s\)) is:

\[ s = \sqrt{\frac{1}{n-1} \sum_{i=1}^{n} (X_i - \bar{X})^2} \]

where:
- \(X_i\) represents each individual observation
- \(\bar{X}\) is the mean of the observations
- \(n\) is the total number of observations

A larger standard deviation indicates that the data points are more spread out, while a smaller standard deviation suggests that the data points are closer to the mean.

Distributions

We will also examine the histograms and apply log transformations for key variables to assess whether the transformed variables follow a more normal-like distribution.

Histograms provide a visual representation of how a variable’s values are distributed, helping to identify whether the data follows a normal distribution, is right-skewed, or left-skewed. Linear regression model assume that variables are approximately normally distributed.

  • X-axis: The values of the variable (e.g., house prices, income levels).
  • Y-axis: The frequency of observations within each bin.

If the histogram is right-skewed, it suggests that a small number of observations have significantly higher values compared to the rest. For variables following a right-skewed distribution, we will apply a log transformation to these variables to improve normality. Since the log transformation is undefined for zero or negative values, we must first check whether any variable contains zero.
- If no zeros are present, we apply the standard log transformation:
\[ X' = \log_{10} (X) \]
- If zeros are present, we adjust by adding 1 before taking the log:
\[ X' = \log_{10} (X + 1) \]
This ensures that all values remain positive and avoids undefined values.

By comparing the original histograms with the log-transformed histograms, we will assess whether the transformation improves the suitability of the data for predictive modeling.

Correlations

We will analyze correlations between predictors, to detect potential multicollinearity before proceeding with regression analysis, which can distort model interpretations.

Multicollinearity occurs when predictors are highly correlated, which can lead to unstable regression coefficients, making interpretation difficult. It also inflates standard errors, reducing the statistical significance of predictors, and increases the risk of overfitting, as redundant variables do not contribute new information to the model.

The correlation coefficient \(r\) is calculated as:

\[ r = \frac{\sum (X_i - \bar{X}) (Y_i - \bar{Y})}{\sqrt{\sum (X_i - \bar{X})^2 \cdot \sum (Y_i - \bar{Y})^2}} \]

where:
- \(X_i\) and \(Y_i\) are individual data points for variables \(X\) and \(Y\), respectively.
- \(\bar{X}\) and \(\bar{Y}\) are the mean values of \(X\) and \(Y\).
- The numerator represents the covariance between \(X\) and \(Y\), while the denominator normalizes the values.

The correlation coefficient \(r\) ranges from -1 to 1:
- \(r = 1\): Represents a perfect positive correlation, where an increase in 𝑋 corresponds to a increase in 𝑌. - \(r = -1\): Represents a perfect negative correlation, where an increase in 𝑋 corresponds to a decrease in 𝑌.
- \(r = 0\): No linear correlation, meaning changes in 𝑋 do not predict changes in 𝑌.

Results

Exploratory Results

dependent_var <- "MEDHVAL"

predictors <- c("PCTBACHMOR", "NBELPOV100", "PCTVACANT", "PCTSINGLES")

summary_stats <- data %>%
  dplyr::select(all_of(c(dependent_var, predictors))) %>%
  summarise_all(list(Mean = mean, SD = sd), na.rm = TRUE) %>%
  pivot_longer(cols = everything(), names_to = "Variable", values_to = "Value") %>%
  separate(Variable, into = c("Variable", "Stat"), sep = "_") %>%
  pivot_wider(names_from = Stat, values_from = Value)



summary_stats$Variable <- recode(summary_stats$Variable,
  "MEDHVAL" = "Median House Value",
  "NBELPOV100" = "# Households Living in Poverty",
  "PCTBACHMOR" = "% of Individuals with Bachelor’s Degrees or Higher",
  "PCTVACANT" = "% of Vacant Houses",
  "PCTSINGLES" = "% of Single House Units"
)



summary_stats <- summary_stats %>%
  mutate(
    Mean = round(Mean, 2),
    SD = round(SD, 2)
  )

summary_stats <- summary_stats %>%
  arrange(Variable == "Median House Value")

predictor_rows <- which(summary_stats$Variable != "Median House Value")
dependent_rows <- which(summary_stats$Variable == "Median House Value")

# Determine the start and end rows for each group
start_pred <- min(predictor_rows)
end_pred   <- max(predictor_rows)
start_dep  <- min(dependent_rows)
end_dep    <- max(dependent_rows)

# Create the table using kable and add extra formatting
kable(summary_stats, caption = "Summary Statistics", 
      align = c("l", "l", "l"), booktabs = TRUE, escape = FALSE ) %>%
  add_header_above(c(" " = 1, "Statistics" = 2)) %>%
  kable_styling(full_width = FALSE) %>%
  group_rows("Predictors", start_pred, end_pred) %>%
  group_rows("Dependent Variable", start_dep, end_dep)%>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = TRUE)
Summary Statistics
Statistics
Variable Mean SD
Predictors
% of Individuals with Bachelor’s Degrees or Higher 16.08 17.77
# Households Living in Poverty 189.77 164.32
% of Vacant Houses 11.29 9.63
% of Single House Units 9.23 13.25
Dependent Variable
Median House Value 66287.73 60006.08
#check 0
columns_to_check <- c(dependent_var, predictors)

zero_counts <- sapply(data[columns_to_check], function(x) sum(x == 0, na.rm = TRUE))

zero_counts[zero_counts > 0]
## PCTBACHMOR NBELPOV100  PCTVACANT PCTSINGLES 
##        143         33        163        306
data <- data %>%
  mutate(
    LNMEDHVAL = log(MEDHVAL),
    LNPCTBACHMOR = log(1+PCTBACHMOR),
    LNNBELPOV100 = log(1+NBELPOV100),
    LNPCTVACANT = log(1+PCTVACANT),
    LNPCTSINGLES = log(1+PCTSINGLES)
  )
longer_version<- data %>%
  pivot_longer(cols = c("MEDHVAL", "PCTBACHMOR", "NBELPOV100", "PCTVACANT", "PCTSINGLES"),
               names_to = "Variable",
               values_to = "Value")

ggplot(longer_version,aes(x = Value)) +
  geom_histogram(aes(y = ..count..), fill = "black", alpha = 0.7) +  
  facet_wrap(~Variable, scales = "free", ncol = 3, labeller = as_labeller(c(
    "MEDHVAL" = "Median House Value",
    "PCTBACHMOR" = "% with Bachelor’s Degrees or Higher",
    "NBELPOV100" = "# Households Living in Poverty",
    "PCTVACANT" = "% of Vacant Houses",
    "PCTSINGLES" = "% of Single House Units"
  ))) +  
  labs(x = "Value", y = "Count", title = "Histograms of Dependent and Predictor Variables") +
  theme_light() +   
  theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x=element_text(size=6),
        axis.text.y=element_text(size=6), 
        axis.title=element_text(size=8))

# histograms of the transformed variables
longer_version2 <- data %>%
  pivot_longer(cols = c(LNMEDHVAL, LNPCTBACHMOR ,LNNBELPOV100,LNPCTVACANT, LNPCTSINGLES),
               names_to = "Variable",
               values_to = "Value")

ggplot(longer_version2,aes(x = Value)) +
  geom_histogram(aes(y = ..count..), fill = "red", alpha = 0.7) +  
  facet_wrap(~Variable, scales = "free", ncol = 3, labeller = as_labeller(c(
    "LNMEDHVAL" = "Log Median House Value",
    "LNPCTBACHMOR" = "Log % with Bachelor’s Degree",
    "LNNBELPOV100" = "Log # Households in Poverty",
    "LNPCTVACANT" = "Log % Vacant Houses",
    "LNPCTSINGLES" = "Log % Single House Units"
  ))) +  
  labs(x = "Value", y = "Count", title = "Histograms of Dependent and log transformed Predictor Variables") +
  theme_light() +   
  theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x=element_text(size=6),
        axis.text.y=element_text(size=6), 
        axis.title=element_text(size=8))

ggplot(shape) +
  geom_sf(aes(fill = LNMEDHVAL), color = "transparent") +
  scale_fill_gradientn(colors = c("#fff0f3", "#a4133c"), 
                       name = "LNMEDHVAL", 
                       na.value = "transparent") + 
  theme(legend.text = element_text(size = 9),
        legend.title = element_text(size = 10),
        axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks.y = element_blank(),
        plot.subtitle = element_text(size = 9, face = "italic"),
        plot.title = element_text(size = 12, face = "bold"),
        panel.background = element_blank(),
        panel.border = element_rect(colour = "grey", fill = NA, size = 0.8)) +
  labs(title = "Log Transformed Median House Value")

shpe_longer<- shape %>%
  pivot_longer(cols = c("PCTVACANT", "PCTSINGLES", "PCTBACHMOR", "LNNBELPOV"),
               names_to = "Variable",
               values_to = "Value")
custom_titles <- c(
  PCTVACANT   = "Percent of Vacant Houses",
  PCTSINGLES  = "Percent of Single House Units",
  PCTBACHMOR  = "Percent of Bachelor's Degree or Higher",
  LNNBELPOV   = "Logged Transformed Poverty Rate"
)



plot_list <- lapply(unique(shpe_longer$Variable), function(var_name) {
  data_subset <- subset(shpe_longer, Variable == var_name)
  
  ggplot(data_subset) +
    geom_sf(aes(fill = Value), color = "transparent") +
    scale_fill_gradientn(
      colors = c("#fff0f3", "#a4133c"),
      name = var_name,
      na.value = "transparent"
    ) +
    labs(title = custom_titles[[var_name]]) +
    theme(
      legend.text = element_text(size = 8),
      legend.title = element_text(size = 10),
      legend.key.size = unit(0.3, "cm"),
      axis.text.x = element_blank(),
      axis.ticks.x = element_blank(),
      axis.text.y = element_blank(),
      axis.ticks.y = element_blank(),
      plot.subtitle = element_text(size = 9, face = "italic"),
      plot.title = element_text(size = 15, face = "bold"),
      panel.background = element_blank(),
      panel.border = element_rect(colour = "grey", fill = NA, size = 0.8)
    )
})

# Combine the plots into a grid (2 columns by 2 rows)
combined_plot <- (plot_list[[1]] + plot_list[[2]]) /
                 (plot_list[[3]] + plot_list[[4]])

combined_plot

fit <- lm(LNMEDHVAL ~ PCTVACANT + PCTSINGLES + PCTBACHMOR + LNNBELPOV100, data=data)
summary(fit)
## 
## Call:
## lm(formula = LNMEDHVAL ~ PCTVACANT + PCTSINGLES + PCTBACHMOR + 
##     LNNBELPOV100, data = data)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -2.25825 -0.20391  0.03822  0.21744  2.24347 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  11.1137661  0.0465330 238.836  < 2e-16 ***
## PCTVACANT    -0.0191569  0.0009779 -19.590  < 2e-16 ***
## PCTSINGLES    0.0029769  0.0007032   4.234 2.42e-05 ***
## PCTBACHMOR    0.0209098  0.0005432  38.494  < 2e-16 ***
## LNNBELPOV100 -0.0789054  0.0084569  -9.330  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.3665 on 1715 degrees of freedom
## Multiple R-squared:  0.6623, Adjusted R-squared:  0.6615 
## F-statistic: 840.9 on 4 and 1715 DF,  p-value: < 2.2e-16
anova_table <- anova(fit)
anova_table
## Analysis of Variance Table
## 
## Response: LNMEDHVAL
##                Df  Sum Sq Mean Sq  F value    Pr(>F)    
## PCTVACANT       1 180.392 180.392 1343.087 < 2.2e-16 ***
## PCTSINGLES      1  24.543  24.543  182.734 < 2.2e-16 ***
## PCTBACHMOR      1 235.118 235.118 1750.551 < 2.2e-16 ***
## LNNBELPOV100    1  11.692  11.692   87.054 < 2.2e-16 ***
## Residuals    1715 230.344   0.134                       
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
fitted_values <- fitted(fit)
residuals_values <- residuals(fit)
standardized_residuals <- rstandard(fit)

data <- data %>%
  mutate(
    Fitted = fitted_values,
    Residuals = residuals_values,
    Standardized_Residuals = standardized_residuals)
ggplot(data, aes(x = Fitted, y = Standardized_Residuals)) +
  geom_point(color = "black", size= 0.4) +    
  geom_hline(yintercept = 0, linetype = "dashed", color = "red") +  
  labs(
    title = "Scatter Plot of Standardized Residuals vs Fitted Values",
    x = "Predicted Values",
    y = "Standardized Residuals"
  ) +
  theme_minimal() +   
  theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x=element_text(size=6),
        axis.text.y=element_text(size=6), 
        axis.title=element_text(size=8))

ggplot(data, aes(x = Standardized_Residuals)) +
  geom_histogram(bins = 30, fill = "black") +
  labs(title = "Histogram of Standardized Residuals", 
       x = "Standardized Residuals", 
       y = "Frequency") +
  theme_minimal() +   
  theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x=element_text(size=6),
        axis.text.y=element_text(size=6), 
        axis.title=element_text(size=8))

longer<-data %>%
  pivot_longer(cols = c("PCTBACHMOR", "LNNBELPOV100", "PCTVACANT", "PCTSINGLES"),
               names_to = "Variable",
               values_to = "Value")

ggplot(longer,aes(x = Value, y = LNMEDHVAL)) +
  geom_point(color = "black", size= 0.4) +
  geom_smooth(method = "lm", color = "red", se = FALSE) + 
  facet_wrap(~ Variable, scales = "free", labeller = as_labeller(c(
    "PCTBACHMOR" = "% with Bachelor’s Degrees or Higher",
    "LNNBELPOV100" = "Logged Households Living in Poverty",
    "PCTVACANT" = "% of Vacant Houses",
    "PCTSINGLES" = "% of Single House Units"
  )))  +
  theme_light() +   
  theme(plot.subtitle = element_text(size = 9,face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x=element_text(size=6),
        axis.text.y=element_text(size=6), 
        axis.title=element_text(size=8)) +
  labs(title = "Scatter Plots of Dependent Variable vs. Predictors", 
       x = "Predictor Value", 
       y = "Log of Median House Value")

join<- data %>%
  dplyr::select(POLY_ID, Standardized_Residuals)

shape <- shape %>%
  left_join(join, by = c("POLY_ID" = "POLY_ID"))

ggplot(shape)+
  geom_sf(aes(fill = Standardized_Residuals), color = "transparent") +
  scale_fill_gradientn(colors = c("#fff0f3", "#a4133c"), 
                       name = "Std Residuals", 
                       na.value = "transparent") +  # Choose a color palette, invert direction if needed
  labs(title = "Choropleth Map of Standardized Residuals") +
  theme(legend.text = element_text(size = 9),
        legend.title = element_text(size = 10),
        axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks.y = element_blank(),
        plot.subtitle = element_text(size = 9, face = "italic"),
        plot.title = element_text(size = 12, face = "bold"),
        panel.background = element_blank(),
        panel.border = element_rect(colour = "grey", fill = NA, size = 0.8))

custom_labels <- c(
  "% of Individuals with Bachelor’s Degrees or Higher" = "PCTBACHMOR",
  "% of Vacant Houses" = "PCTVACANT",
  "% of Single House Units" = "PCTSINGLES",
  "# Households Living in Poverty" = "LNNBELPOV100"
)

predictor_vars <- data[, c("PCTVACANT", "PCTSINGLES", "PCTBACHMOR", "LNNBELPOV100")]

cor_matrix <- cor(predictor_vars, use = "complete.obs", method = "pearson")

print(cor_matrix)
##               PCTVACANT PCTSINGLES PCTBACHMOR LNNBELPOV100
## PCTVACANT     1.0000000 -0.1513734 -0.2983580    0.2495470
## PCTSINGLES   -0.1513734  1.0000000  0.1975461   -0.2905159
## PCTBACHMOR   -0.2983580  0.1975461  1.0000000   -0.3197668
## LNNBELPOV100  0.2495470 -0.2905159 -0.3197668    1.0000000
rownames(cor_matrix) <- names(custom_labels)
colnames(cor_matrix) <- names(custom_labels)


ggcorrplot(cor_matrix, 
           method = "square",   
           type = "lower",      
           lab = TRUE,       
           lab_size = 3,      
           colors = c("#d73027", "white", "#1a9850"))+
    labs(title = "Correlation Matrix for all Predictor Variables") +
    theme(plot.subtitle = element_text(size = 9, face = "italic"),
        plot.title = element_text(size = 12, face = "bold"), 
        axis.text.x = element_text(size = 7),
        axis.text.y = element_text(size = 7), 
        axis.title = element_text(size = 8))

stepwise_model <-  stepAIC(fit, direction = "both")
## Start:  AIC=-3448.07
## LNMEDHVAL ~ PCTVACANT + PCTSINGLES + PCTBACHMOR + LNNBELPOV100
## 
##                Df Sum of Sq    RSS     AIC
## <none>                      230.34 -3448.1
## - PCTSINGLES    1     2.407 232.75 -3432.2
## - LNNBELPOV100  1    11.692 242.04 -3364.9
## - PCTVACANT     1    51.546 281.89 -3102.7
## - PCTBACHMOR    1   199.020 429.36 -2379.0
stepwise_model$anova
## Stepwise Model Path 
## Analysis of Deviance Table
## 
## Initial Model:
## LNMEDHVAL ~ PCTVACANT + PCTSINGLES + PCTBACHMOR + LNNBELPOV100
## 
## Final Model:
## LNMEDHVAL ~ PCTVACANT + PCTSINGLES + PCTBACHMOR + LNNBELPOV100
## 
## 
##   Step Df Deviance Resid. Df Resid. Dev       AIC
## 1                       1715   230.3435 -3448.073
lm <-  trainControl(method = "cv", number = 5)

cvlm_model <- train(LNMEDHVAL ~ PCTVACANT + PCTSINGLES + PCTBACHMOR + LNNBELPOV100, data=data, method = "lm", trControl = lm)

print(cvlm_model)
## Linear Regression 
## 
## 1720 samples
##    4 predictor
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold) 
## Summary of sample sizes: 1376, 1376, 1376, 1376, 1376 
## Resampling results:
## 
##   RMSE       Rsquared   MAE      
##   0.3669369  0.6609121  0.2725123
## 
## Tuning parameter 'intercept' was held constant at a value of TRUE
cvlm_model_reduced = train(LNMEDHVAL ~ PCTVACANT + MEDHHINC, data = data, method = "lm", trControl = lm)

print(cvlm_model_reduced)
## Linear Regression 
## 
## 1720 samples
##    2 predictor
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold) 
## Summary of sample sizes: 1376, 1376, 1376, 1376, 1376 
## Resampling results:
## 
##   RMSE      Rsquared   MAE      
##   0.441732  0.5095327  0.3175378
## 
## Tuning parameter 'intercept' was held constant at a value of TRUE
LS0tCnRpdGxlOiAnVXNpbmcgT0xTIFJlZ3Jlc3Npb24gdG8gUHJlZGljdCBNZWRpYW4gSG91c2UgVmFsdWVzIGluIFBoaWxhZGVscGhpYScKYXV0aG9yOiAiWmhhbmNoYW8gWWFuZywgSGFveXUgWmh1LCBLYXZhbmEgUmFqdSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogZmxhdGx5CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQoKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc2YpCmxpYnJhcnkodGlkeWNlbnN1cykKbGlicmFyeShrbml0cikgCmxpYnJhcnkoZ3QpIApsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoZ2djb3JycGxvdCkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoTUFTUykKbGlicmFyeShjYXJldCkKYGBgCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgaW5jbHVkZT0gRkFMU0V9CiMgTG9hZCB0aGUgZGF0YQpkYXRhIDwtIHJlYWQuY3N2KCJkYXRhL1JlZ3Jlc3Npb25EYXRhLmNzdiIpCnNoYXBlIDwtIHN0X3JlYWQoImRhdGEvUmVncmVzc2lvbkRhdGEuc2hwIikKYGBgCgojIEludHJvZHVjdGlvbgpJbiByZWNlbnQgZGVjYWRlcywgUGhpbGFkZWxwaGlhIGhhcyBleHBlcmllbmNlZCBzdWJzdGFudGlhbCBjaGFuZ2VzIGluIHBvcHVsYXRpb24gZHluYW1pY3MsIGVjb25vbWljIGZsdWN0dWF0aW9ucywgYW5kIHJlZGV2ZWxvcG1lbnQgcHJlc3N1cmVzLiBBbW9uZyB0aGVzZSBjaGFuZ2VzLCBob3VzaW5nIHZhbHVlcyBoYXZlIGJlY29tZSBhIGtleSBpbmRpY2F0b3Igb2YgdGhlIHNvY2lvLWVjb25vbWljIGNvbmRpdGlvbnMgd2l0aGluIHRoZSBjaXR54oCZcyBuZWlnaGJvcmhvb2RzLiBSaXNpbmcgaG91c2luZyB2YWx1ZXMgdHlwaWNhbGx5IHJlZmxlY3QgYSBoaWdoZXIgcHJlc2VuY2Ugb2Ygd2VhbHRoaWVyIHJlc2lkZW50cyBvciBhIG5laWdoYm9yaG9vZCB1bmRlcmdvaW5nIGdlbnRyaWZpY2F0aW9uLCB3aGlsZSBkZWNsaW5pbmcgaG91c2luZyB2YWx1ZXMgb2Z0ZW4gc2lnbmFsIGluY3JlYXNlZCBlY29ub21pYyBwcmVzc3VyZSBvciBkZWNsaW5lLiBUaGVyZWZvcmUsIGFjY3VyYXRlbHkgZm9yZWNhc3RpbmcgdGhlIG1lZGlhbiBob3VzaW5nIHZhbHVlcyBhY3Jvc3MgUGhpbGFkZWxwaGlhIGlzIGNydWNpYWwgZm9yIHVyYmFuIHBsYW5uZXJzIGFuZCBwb2xpY3ltYWtlcnMsIGhlbHBpbmcgdGhlbSB0byBwcm9hY3RpdmVseSBhZGRyZXNzIHBvdGVudGlhbCByaXNrcyBzdWNoIGFzIGRpc3BsYWNlbWVudCBhbmQgZGlzaW52ZXN0bWVudC4gU3VwcG9ydGluZyBQaGlsYWRlbHBoaWHigJlzIGxvbmctdGVybSB2aXNpb24gb2YgZm9zdGVyaW5nIHZpYnJhbnQsIGRpdmVyc2UsIGFuZCByZXNpbGllbnQgY29tbXVuaXRpZXMgaXMgYWxzbyBlc3NlbnRpYWwuCgpUbyBiZXR0ZXIgdW5kZXJzdGFuZCB0aGUgcG90ZW50aWFsIGZhY3RvcnMgaW5mbHVlbmNpbmcgaG91c2luZyB2YWx1ZXMgaW4gUGhpbGFkZWxwaGlhLCB3ZSBpZGVudGlmeSBzZXZlcmFsIGtleSB2YXJpYWJsZXM6IGVkdWNhdGlvbmFsIGF0dGFpbm1lbnQsIHZhY2FuY3kgcmF0ZXMsIHRoZSBwcm9wb3J0aW9uIG9mIHNpbmdsZS1mYW1pbHkgaG9tZXMsIGFuZCBwb3ZlcnR5IGxldmVscy4gVGhlc2UgZmFjdG9ycyBhcmUgY2xvc2VseSByZWxhdGVkIHRvIGhvdXNpbmcgbWFya2V0IHRyZW5kcyBhbmQgcHJvdmlkZSBpbnNpZ2h0cyBpbnRvIHRoZSBuZWlnaGJvcmhvb2TigJlzIHNvY2lvLWVjb25vbWljIHN0YXR1cy4gCgpFZHVjYXRpb25hbCBhdHRhaW5tZW50IGlzIGNsb3NlbHkgbGlua2VkIHRvIHRoZSBzb2Npby1lY29ub21pYyBjaGFyYWN0ZXJpc3RpY3Mgb2YgbmVpZ2hib3Job29kcy4gSW5kaXZpZHVhbHMgd2l0aCBoaWdoZXIgZWR1Y2F0aW9uYWwgYXR0YWlubWVudCB0eXBpY2FsbHkgZWFybiBoaWdoZXIgaW5jb21lcyBhbmQgY29udHJpYnV0ZSBtb3JlIHRvIGxvY2FsIGVjb25vbWllcy4gQSBoaWdoZXIgY29uY2VudHJhdGlvbiBvZiBpbmRpdmlkdWFscyB3aXRoIGFkdmFuY2VkIGVkdWNhdGlvbmFsIGJhY2tncm91bmRzIHRlbmRzIHRvIGluY3JlYXNlIHRoZSBkZW1hbmQgZm9yIHdlbGwtZGVzaWduZWQgaG91c2luZyBpbiBhZmZsdWVudCBuZWlnaGJvcmhvb2RzLiBXaXRoIGEgY29uc2lzdGVudCBzdXBwbHksIGhvdXNpbmcgcHJpY2VzIGFuZCB2YWx1ZXMgYXJlIGxpa2VseSB0byByaXNlLiBTaW1pbGFybHksIGEgaGlnaCBwb3ZlcnR5IHJhdGUgYWxzbyBpbmRpY2F0ZXMgYSBkZWNsaW5pbmcgY29tbXVuaXR5LiBTaW5jZSBtYW55IHJlc2lkZW50cyBjYW5ub3QgYWZmb3JkIGx1eHVyeSBob3VzaW5nLCB0aGUgaG91c2luZyB2YWx1ZXMgaW4gdGhvc2UgbmVpZ2hib3Job29kcyBhcmUgbGlrZWx5IHRvIGJlIExvd2VyLgoKCkhpZ2ggdmFjYW5jeSByYXRlcyBvZnRlbiBjb3JyZWxhdGUgd2l0aCBkZWNsaW5pbmcgbmVpZ2hib3Job29kcyBhbmQgcmVkdWNlZCBtZWRpYW4gaG91c2UgdmFsdWVzLiBSZXNlYXJjaCBzaG93cyB0aGF0IHZhY2FudCBwcm9wZXJ0aWVzIGFmZmVjdCBtdWx0aXBsZSBmYWNldHMgb2YgY29tbXVuaXR5IGxpZmUsIHN1Y2ggYXMgaG91c2luZyBhbmQgbmVpZ2hib3Job29kIHZpdGFsaXR5LCBjcmltZSBwcmV2ZW50aW9uIGVmZm9ydHMsIGFuZCB0aGUgd2VsbC1iZWluZyBvZiBjb21tZXJjaWFsIGRpc3RyaWN0cy4gQXMgYSByZXN1bHQsIGFyZWFzIHdpdGggbnVtZXJvdXMgdmFjYW50IGhvdXNpbmcgdW5pdHMgdHlwaWNhbGx5IHNlZSBkaW1pbmlzaGVkIG1lZGlhbiBob3VzaW5nIHZhbHVlcy4gIAoKClRoZSBwcm9wb3J0aW9uIG9mIHNpbmdsZS1mYW1pbHkgaG9tZXMgaW4gYW4gYXJlYSBpbmZsdWVuY2VzIGhvdXNpbmcgdmFsdWVzLiBUaGVzZSBob21lcyBhcmUgZ2VuZXJhbGx5IHByaXZhdGUgYW5kIGNvbWZvcnRhYmxlLCBtYWtpbmcgdGhlbSBtb3JlIGRlc2lyYWJsZSBpbiBtYW55IFUuUy4gaG91c2luZyBtYXJrZXRzLiAgSG93ZXZlciwgdGhleSBhcmUgcmVsYXRpdmVseSBjb21tb24gaW4gc3VidXJiYW4gbmVpZ2hib3Job29kcyBhbmQgbWF5IHN1ZmZlciBmcm9tIGxvdyBhY2Nlc3NpYmlsaXR5IGFuZCBsaW1pdGVkIGluZnJhc3RydWN0dXJlLCB3aGljaCBjYW4gbmVnYXRpdmVseSBhZmZlY3QgdGhlaXIgcHJvcGVydHkgdmFsdWVzIGFzIHdlbGwuCgpJbiB0aGlzIHN0dWR5LCB3ZSB1dGlsaXplIG9yZGluYXJ5IGxlYXN0IHNxdWFyZXMgKE9MUykgcmVncmVzc2lvbiB0byBhbmFseXplIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGVzZSBzb2Npb2Vjb25vbWljIGZhY3RvcnMgYW5kIG1lZGlhbiBob3VzZSB2YWx1ZXMgaW4gUGhpbGFkZWxwaGlhLiBCeSBleGFtaW5pbmcgdGhlc2UgcmVsYXRpb25zaGlwcywgd2UgYWltIHRvIGlkZW50aWZ5IGNyaXRpY2FsIHByZWRpY3RvcnMgb2YgbWVkaWFuIGhvdXNpbmcgdmFsdWVzIHRocm91Z2hvdXQgUGhpbGFkZWxwaGlhIGFuZCBvZmZlciBpbnNpZ2h0cyBmb3IgZGVjaXNpb24tbWFrZXJzIGFuZCBjb21tdW5pdHkgaW5pdGlhdGl2ZXMuIAoKIyBNZXRob2RzCgojIyBEYXRhIENsZWFuaW5nCgpUbyBwcmVkaWN0IG1lZGlhbiBob3VzZSB2YWx1ZXMgaW4gUGhpbGFkZWxwaGlhLCB3ZSBvYnRhaW5lZCB0aGUgb3JpZ2luYWwgZGF0YXNldCBmcm9tIHRoZSBVbml0ZWQgU3RhdGVzIENlbnN1cyBkYXRhLiBUaGUgZGF0YXNldCByZXByZXNlbnRzIGNlbnN1cyBibG9jayBncm91cHMgZnJvbSB0aGUgeWVhciAyMDAwIGFuZCBpbml0aWFsbHkgY29udGFpbmVkIDEsODE2IG9ic2VydmF0aW9ucy4gVGhlIGtleSB2YXJpYWJsZXMgaW5jbHVkZWQ6CgotICAqUE9MWV9JRCog4oCTIENlbnN1cyBCbG9jayBHcm91cCBJRAotICAqTUVESFZBTCog4oCTIE1lZGlhbiB2YWx1ZSBvZiBhbGwgb3duZXItb2NjdXBpZWQgaG91c2luZyB1bml0cwotICAqUENCQUNITU9SRSog4oCTIFByb3BvcnRpb24gb2YgcmVzaWRlbnRzIGluIHRoZSBibG9jayBncm91cCB3aXRoIGF0IGxlYXN0IGEgYmFjaGVsb3LigJlzIGRlZ3JlZQotICAqUENUVkFDQU5UKiDigJMgUHJvcG9ydGlvbiBvZiBob3VzaW5nIHVuaXRzIHRoYXQgYXJlIHZhY2FudAotICAqUENUU0lOR0xFUyog4oCTIFBlcmNlbnRhZ2Ugb2YgaG91c2luZyB1bml0cyB0aGF0IGFyZSBkZXRhY2hlZCBzaW5nbGUtZmFtaWx5IGhvdXNlcwotICAqTkJFTFBPVjEwMCog4oCTIE51bWJlciBvZiBob3VzZWhvbGRzIHdpdGggaW5jb21lcyBiZWxvdyAxMDAlIG9mIHRoZSBwb3ZlcnR5IGxldmVsCi0gICpNRURISElOQyog4oCTIE1lZGlhbiBob3VzZWhvbGQgaW5jb21lCgpUbyByZWZpbmUgdGhlIGRhdGFzZXQgZm9yIG1vZGVsaW5nIHB1cnBvc2VzLCB3ZSBhcHBsaWVkIHRoZSBmb2xsb3dpbmcgZmlsdGVyaW5nIGNyaXRlcmlhOgoKKDEpIFJldGFpbmVkIGJsb2NrIGdyb3VwcyB3aXRoIGEgcG9wdWxhdGlvbiBncmVhdGVyIHRoYW4gNDAKKDIpIEluY2x1ZGVkIG9ubHkgYmxvY2sgZ3JvdXBzIHRoYXQgY29udGFpbmVkIGhvdXNpbmcgdW5pdHMKKDMpIEV4Y2x1ZGVkIHJlY29yZHMgd2hlcmUgdGhlIG1lZGlhbiBob3VzZSB2YWx1ZSB3YXMgYmVsb3cgJDEwLDAwMAoKQWRkaXRpb25hbGx5LCB3ZSByZW1vdmVkIGEgc3BlY2lmaWMgYmxvY2sgZ3JvdXAgaW4gTm9ydGggUGhpbGFkZWxwaGlhIHRoYXQgZXhoaWJpdGVkIGluY29uc2lzdGVuY2llcywgd2l0aCBhbiB1bnVzdWFsbHkgaGlnaCBtZWRpYW4gaG91c2UgdmFsdWUgKG92ZXIgJDgwMCwwMDApIGRlc3BpdGUgYSB2ZXJ5IGxvdyBtZWRpYW4gaG91c2Vob2xkIGluY29tZSAobGVzcyB0aGFuICQ4LDAwMCkuCgpBZnRlciBkYXRhIGNsZWFuaW5nLCB0aGUgZmluYWwgZGF0YXNldCBjb250YWluZWQgMSw3MjAgb2JzZXJ2YXRpb25zLiAKCiMjIEV4cGxvcmF0b3J5IERhdGEgQW5heWxzaXMKCiMjIyBTdW1tYXJ5IFN0YXRpc3RpY3MKV2Ugd2lsbCBmaXJzdCBleGFtaW5lIHRoZSBzdW1tYXJ5IHN0YXRpc3RpY3Mgb2Yga2V5IHZhcmlhYmxlcyBpbiB0aGUgZGF0YXNldCwgaW5jbHVkaW5nIHRoZSBkZXBlbmRlbnQgdmFyaWFibGVzICpNRURIVkFMKiAoTWVkaWFuIEhvdXNlIFZhbHVlKSwgYW5kIHByZWRpY3RvcnMgKk5CRUxQT1YxMDAqIChIb3VzZWhvbGRzIExpdmluZyBpbiBQb3ZlcnR5KSwgKlBDVEJBQ0hNT1IqICglIG9mIEluZGl2aWR1YWxzIHdpdGggQmFjaGVsb3LigJlzIERlZ3JlZXMgb3IgSGlnaGVyKSwgKlBDVFZBQ0FOVCooJSBvZiBWYWNhbnQgSG91c2VzKSwgKlBDVFNJTkdMRVMqKCAlIG9mIFNpbmdsZSBIb3VzZSBVbml0cykuIAoKV2Ugd2lsbCBleGFtaW5lIHRoZSAqKm1lYW4qKiBhbmQgKipzdGFuZGFyZCBkZXZpYXRpb24gKFNEKSoqIG9mIGtleSB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQuICAgCgpUaGUgKiptZWFuKiogKFwoXGJhcntYfVwpKSByZXByZXNlbnRzIHRoZSBhdmVyYWdlIHZhbHVlIG9mIGEgdmFyaWFibGUgYW5kIGlzIGNhbGN1bGF0ZWQgYXM6ICAKCiQkClxiYXJ7WH0gPSBcZnJhY3sxfXtufSBcc3VtX3tpPTF9XntufSBYX2kKJCQgIAoKd2hlcmU6ICAKLSBcKFhfaVwpIHJlcHJlc2VudHMgZWFjaCBpbmRpdmlkdWFsIG9ic2VydmF0aW9uICAKLSBcKG5cKSBpcyB0aGUgdG90YWwgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyAgCgpUaGUgbWVhbiBnaXZlcyB1cyBhIHNpbmdsZSByZXByZXNlbnRhdGl2ZSB2YWx1ZSBvZiB0aGUgZGF0YXNldCwgd2hpY2ggaGVscHMgaW4gdW5kZXJzdGFuZGluZyB0aGUgdHlwaWNhbCB2YWx1ZSBmb3IgYSBnaXZlbiB2YXJpYWJsZS4gIAoKVG8gbWVhc3VyZSB2YXJpYWJpbGl0eSwgd2UgdXNlIHRoZSAqKnN0YW5kYXJkIGRldmlhdGlvbiAoU0QpKiosIHdoaWNoIHF1YW50aWZpZXMgaG93IG11Y2ggdGhlIHZhbHVlcyBpbiBhIGRhdGFzZXQgZGV2aWF0ZSBmcm9tIHRoZSBtZWFuLiBUaGUgZm9ybXVsYSBmb3IgdGhlIHNhbXBsZSBzdGFuZGFyZCBkZXZpYXRpb24gKFwoc1wpKSBpczogIAoKJCQKcyA9IFxzcXJ0e1xmcmFjezF9e24tMX0gXHN1bV97aT0xfV57bn0gKFhfaSAtIFxiYXJ7WH0pXjJ9CiQkICAKCndoZXJlOiAgCi0gXChYX2lcKSByZXByZXNlbnRzIGVhY2ggaW5kaXZpZHVhbCBvYnNlcnZhdGlvbiAgCi0gXChcYmFye1h9XCkgaXMgdGhlIG1lYW4gb2YgdGhlIG9ic2VydmF0aW9ucyAgCi0gXChuXCkgaXMgdGhlIHRvdGFsIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgIAoKQSBsYXJnZXIgc3RhbmRhcmQgZGV2aWF0aW9uIGluZGljYXRlcyB0aGF0IHRoZSBkYXRhIHBvaW50cyBhcmUgbW9yZSBzcHJlYWQgb3V0LCB3aGlsZSBhIHNtYWxsZXIgc3RhbmRhcmQgZGV2aWF0aW9uIHN1Z2dlc3RzIHRoYXQgdGhlIGRhdGEgcG9pbnRzIGFyZSBjbG9zZXIgdG8gdGhlIG1lYW4uICAKCgojIyMgRGlzdHJpYnV0aW9ucyAKCldlIHdpbGwgYWxzbyBleGFtaW5lIHRoZSBoaXN0b2dyYW1zIGFuZCBhcHBseSBsb2cgdHJhbnNmb3JtYXRpb25zIGZvciBrZXkgdmFyaWFibGVzIHRvIGFzc2VzcyB3aGV0aGVyIHRoZSB0cmFuc2Zvcm1lZCB2YXJpYWJsZXMgZm9sbG93IGEgbW9yZSBub3JtYWwtbGlrZSBkaXN0cmlidXRpb24uIAoKKipIaXN0b2dyYW1zKiogcHJvdmlkZSBhIHZpc3VhbCByZXByZXNlbnRhdGlvbiBvZiBob3cgYSB2YXJpYWJsZSdzIHZhbHVlcyBhcmUgZGlzdHJpYnV0ZWQsIGhlbHBpbmcgdG8gaWRlbnRpZnkgd2hldGhlciB0aGUgZGF0YSBmb2xsb3dzIGEgKipub3JtYWwgZGlzdHJpYnV0aW9uKiosIGlzICoqcmlnaHQtc2tld2VkKiosIG9yICoqbGVmdC1za2V3ZWQqKi4gTGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgYXNzdW1lIHRoYXQgdmFyaWFibGVzIGFyZSBhcHByb3hpbWF0ZWx5IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiAgCgotICoqWC1heGlzOioqIFRoZSB2YWx1ZXMgb2YgdGhlIHZhcmlhYmxlIChlLmcuLCBob3VzZSBwcmljZXMsIGluY29tZSBsZXZlbHMpLiAgCi0gKipZLWF4aXM6KiogVGhlIGZyZXF1ZW5jeSBvZiBvYnNlcnZhdGlvbnMgd2l0aGluIGVhY2ggYmluLiAgCgpJZiB0aGUgaGlzdG9ncmFtIGlzICoqcmlnaHQtc2tld2VkKiosIGl0IHN1Z2dlc3RzIHRoYXQgYSBzbWFsbCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGhhdmUgc2lnbmlmaWNhbnRseSBoaWdoZXIgdmFsdWVzIGNvbXBhcmVkIHRvIHRoZSByZXN0LiBGb3IgdmFyaWFibGVzIGZvbGxvd2luZyBhICoqcmlnaHQtc2tld2VkIGRpc3RyaWJ1dGlvbioqLCB3ZSB3aWxsIGFwcGx5IGEgKipsb2cgdHJhbnNmb3JtYXRpb24qKiB0byB0aGVzZSB2YXJpYWJsZXMgdG8gaW1wcm92ZSBub3JtYWxpdHkuIFNpbmNlIHRoZSAqKmxvZyB0cmFuc2Zvcm1hdGlvbioqIGlzIHVuZGVmaW5lZCBmb3IgemVybyBvciBuZWdhdGl2ZSB2YWx1ZXMsIHdlIG11c3QgZmlyc3QgY2hlY2sgd2hldGhlciBhbnkgdmFyaWFibGUgY29udGFpbnMgemVyby4gIAotIElmICoqbm8gemVyb3MqKiBhcmUgcHJlc2VudCwgd2UgYXBwbHkgdGhlIHN0YW5kYXJkIGxvZyB0cmFuc2Zvcm1hdGlvbjogIAokJAogIFgnID0gXGxvZ197MTB9IChYKQokJCAgCi0gSWYgKip6ZXJvcyBhcmUgcHJlc2VudCoqLCB3ZSBhZGp1c3QgYnkgYWRkaW5nIDEgYmVmb3JlIHRha2luZyB0aGUgbG9nOiAgCiQkCiAgWCcgPSBcbG9nX3sxMH0gKFggKyAxKQokJCAgCiAgVGhpcyBlbnN1cmVzIHRoYXQgYWxsIHZhbHVlcyByZW1haW4gcG9zaXRpdmUgYW5kIGF2b2lkcyB1bmRlZmluZWQgdmFsdWVzLiAgCgpCeSBjb21wYXJpbmcgdGhlICoqb3JpZ2luYWwgaGlzdG9ncmFtcyoqIHdpdGggdGhlICoqbG9nLXRyYW5zZm9ybWVkIGhpc3RvZ3JhbXMqKiwgd2Ugd2lsbCBhc3Nlc3Mgd2hldGhlciB0aGUgdHJhbnNmb3JtYXRpb24gaW1wcm92ZXMgdGhlIHN1aXRhYmlsaXR5IG9mIHRoZSBkYXRhIGZvciBwcmVkaWN0aXZlIG1vZGVsaW5nLgoKCiMjIyBDb3JyZWxhdGlvbnMKCldlIHdpbGwgYW5hbHl6ZSAqKmNvcnJlbGF0aW9ucyoqIGJldHdlZW4gcHJlZGljdG9ycywgdG8gZGV0ZWN0IHBvdGVudGlhbCAqKm11bHRpY29sbGluZWFyaXR5KiogYmVmb3JlIHByb2NlZWRpbmcgd2l0aCByZWdyZXNzaW9uIGFuYWx5c2lzLCB3aGljaCBjYW4gZGlzdG9ydCBtb2RlbCBpbnRlcnByZXRhdGlvbnMuICAKCk11bHRpY29sbGluZWFyaXR5IG9jY3VycyB3aGVuIHByZWRpY3RvcnMgYXJlIGhpZ2hseSBjb3JyZWxhdGVkLCB3aGljaCBjYW4gbGVhZCB0byB1bnN0YWJsZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cywgbWFraW5nIGludGVycHJldGF0aW9uIGRpZmZpY3VsdC4gSXQgYWxzbyBpbmZsYXRlcyBzdGFuZGFyZCBlcnJvcnMsIHJlZHVjaW5nIHRoZSBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2Ugb2YgcHJlZGljdG9ycywgYW5kIGluY3JlYXNlcyB0aGUgcmlzayBvZiBvdmVyZml0dGluZywgYXMgcmVkdW5kYW50IHZhcmlhYmxlcyBkbyBub3QgY29udHJpYnV0ZSBuZXcgaW5mb3JtYXRpb24gdG8gdGhlIG1vZGVsLiAgCgpUaGUgKipjb3JyZWxhdGlvbiBjb2VmZmljaWVudCoqIFwoclwpIGlzIGNhbGN1bGF0ZWQgYXM6ICAKCiQkCnIgPSBcZnJhY3tcc3VtIChYX2kgLSBcYmFye1h9KSAoWV9pIC0gXGJhcntZfSl9e1xzcXJ0e1xzdW0gKFhfaSAtIFxiYXJ7WH0pXjIgXGNkb3QgXHN1bSAoWV9pIC0gXGJhcntZfSleMn19CiQkCgp3aGVyZTogIAotIFwoWF9pXCkgYW5kIFwoWV9pXCkgYXJlIGluZGl2aWR1YWwgZGF0YSBwb2ludHMgZm9yIHZhcmlhYmxlcyBcKFhcKSBhbmQgXChZXCksIHJlc3BlY3RpdmVseS4gIAotIFwoXGJhcntYfVwpIGFuZCBcKFxiYXJ7WX1cKSBhcmUgdGhlICoqbWVhbiB2YWx1ZXMqKiBvZiBcKFhcKSBhbmQgXChZXCkuICAKLSBUaGUgbnVtZXJhdG9yIHJlcHJlc2VudHMgdGhlICoqY292YXJpYW5jZSoqIGJldHdlZW4gXChYXCkgYW5kIFwoWVwpLCB3aGlsZSB0aGUgZGVub21pbmF0b3IgKipub3JtYWxpemVzKiogdGhlIHZhbHVlcy4gIAoKVGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IFwoclwpIHJhbmdlcyBmcm9tICoqLTEgdG8gMSoqOiAgCi0gXChyID0gMVwpOiBSZXByZXNlbnRzIGEgcGVyZmVjdCBwb3NpdGl2ZSBjb3JyZWxhdGlvbiwgd2hlcmUgYW4gaW5jcmVhc2UgaW4g8J2RiyBjb3JyZXNwb25kcyB0byBhIGluY3JlYXNlIGluIPCdkYwuCi0gXChyID0gLTFcKTogUmVwcmVzZW50cyBhIHBlcmZlY3QgbmVnYXRpdmUgY29ycmVsYXRpb24sIHdoZXJlIGFuIGluY3JlYXNlIGluIPCdkYsgY29ycmVzcG9uZHMgdG8gYSBkZWNyZWFzZSBpbiDwnZGMLiAgCi0gXChyID0gMFwpOiBObyBsaW5lYXIgY29ycmVsYXRpb24sIG1lYW5pbmcgY2hhbmdlcyBpbiDwnZGLIGRvIG5vdCBwcmVkaWN0IGNoYW5nZXMgaW4g8J2RjC4gIAoKIyBSZXN1bHRzCgojIyBFeHBsb3JhdG9yeSBSZXN1bHRzIAoKCmBgYHtyIHN1bW1hcnkgc3RhdHMsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmRlcGVuZGVudF92YXIgPC0gIk1FREhWQUwiCgpwcmVkaWN0b3JzIDwtIGMoIlBDVEJBQ0hNT1IiLCAiTkJFTFBPVjEwMCIsICJQQ1RWQUNBTlQiLCAiUENUU0lOR0xFUyIpCgpzdW1tYXJ5X3N0YXRzIDwtIGRhdGEgJT4lCiAgZHBseXI6OnNlbGVjdChhbGxfb2YoYyhkZXBlbmRlbnRfdmFyLCBwcmVkaWN0b3JzKSkpICU+JQogIHN1bW1hcmlzZV9hbGwobGlzdChNZWFuID0gbWVhbiwgU0QgPSBzZCksIG5hLnJtID0gVFJVRSkgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gIlZhcmlhYmxlIiwgdmFsdWVzX3RvID0gIlZhbHVlIikgJT4lCiAgc2VwYXJhdGUoVmFyaWFibGUsIGludG8gPSBjKCJWYXJpYWJsZSIsICJTdGF0IiksIHNlcCA9ICJfIikgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IFN0YXQsIHZhbHVlc19mcm9tID0gVmFsdWUpCgoKCnN1bW1hcnlfc3RhdHMkVmFyaWFibGUgPC0gcmVjb2RlKHN1bW1hcnlfc3RhdHMkVmFyaWFibGUsCiAgIk1FREhWQUwiID0gIk1lZGlhbiBIb3VzZSBWYWx1ZSIsCiAgIk5CRUxQT1YxMDAiID0gIiMgSG91c2Vob2xkcyBMaXZpbmcgaW4gUG92ZXJ0eSIsCiAgIlBDVEJBQ0hNT1IiID0gIiUgb2YgSW5kaXZpZHVhbHMgd2l0aCBCYWNoZWxvcuKAmXMgRGVncmVlcyBvciBIaWdoZXIiLAogICJQQ1RWQUNBTlQiID0gIiUgb2YgVmFjYW50IEhvdXNlcyIsCiAgIlBDVFNJTkdMRVMiID0gIiUgb2YgU2luZ2xlIEhvdXNlIFVuaXRzIgopCgoKCnN1bW1hcnlfc3RhdHMgPC0gc3VtbWFyeV9zdGF0cyAlPiUKICBtdXRhdGUoCiAgICBNZWFuID0gcm91bmQoTWVhbiwgMiksCiAgICBTRCA9IHJvdW5kKFNELCAyKQogICkKCnN1bW1hcnlfc3RhdHMgPC0gc3VtbWFyeV9zdGF0cyAlPiUKICBhcnJhbmdlKFZhcmlhYmxlID09ICJNZWRpYW4gSG91c2UgVmFsdWUiKQoKcHJlZGljdG9yX3Jvd3MgPC0gd2hpY2goc3VtbWFyeV9zdGF0cyRWYXJpYWJsZSAhPSAiTWVkaWFuIEhvdXNlIFZhbHVlIikKZGVwZW5kZW50X3Jvd3MgPC0gd2hpY2goc3VtbWFyeV9zdGF0cyRWYXJpYWJsZSA9PSAiTWVkaWFuIEhvdXNlIFZhbHVlIikKCiMgRGV0ZXJtaW5lIHRoZSBzdGFydCBhbmQgZW5kIHJvd3MgZm9yIGVhY2ggZ3JvdXAKc3RhcnRfcHJlZCA8LSBtaW4ocHJlZGljdG9yX3Jvd3MpCmVuZF9wcmVkICAgPC0gbWF4KHByZWRpY3Rvcl9yb3dzKQpzdGFydF9kZXAgIDwtIG1pbihkZXBlbmRlbnRfcm93cykKZW5kX2RlcCAgICA8LSBtYXgoZGVwZW5kZW50X3Jvd3MpCgojIENyZWF0ZSB0aGUgdGFibGUgdXNpbmcga2FibGUgYW5kIGFkZCBleHRyYSBmb3JtYXR0aW5nCmthYmxlKHN1bW1hcnlfc3RhdHMsIGNhcHRpb24gPSAiU3VtbWFyeSBTdGF0aXN0aWNzIiwgCiAgICAgIGFsaWduID0gYygibCIsICJsIiwgImwiKSwgYm9va3RhYnMgPSBUUlVFLCBlc2NhcGUgPSBGQUxTRSApICU+JQogIGFkZF9oZWFkZXJfYWJvdmUoYygiICIgPSAxLCAiU3RhdGlzdGljcyIgPSAyKSkgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UpICU+JQogIGdyb3VwX3Jvd3MoIlByZWRpY3RvcnMiLCBzdGFydF9wcmVkLCBlbmRfcHJlZCkgJT4lCiAgZ3JvdXBfcm93cygiRGVwZW5kZW50IFZhcmlhYmxlIiwgc3RhcnRfZGVwLCBlbmRfZGVwKSU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpLCBmdWxsX3dpZHRoID0gVFJVRSkKCmBgYAoKCgpgYGB7cn0KI2NoZWNrIDAKY29sdW1uc190b19jaGVjayA8LSBjKGRlcGVuZGVudF92YXIsIHByZWRpY3RvcnMpCgp6ZXJvX2NvdW50cyA8LSBzYXBwbHkoZGF0YVtjb2x1bW5zX3RvX2NoZWNrXSwgZnVuY3Rpb24oeCkgc3VtKHggPT0gMCwgbmEucm0gPSBUUlVFKSkKCnplcm9fY291bnRzW3plcm9fY291bnRzID4gMF0KCmBgYAoKYGBge3J9CmRhdGEgPC0gZGF0YSAlPiUKICBtdXRhdGUoCiAgICBMTk1FREhWQUwgPSBsb2coTUVESFZBTCksCiAgICBMTlBDVEJBQ0hNT1IgPSBsb2coMStQQ1RCQUNITU9SKSwKICAgIExOTkJFTFBPVjEwMCA9IGxvZygxK05CRUxQT1YxMDApLAogICAgTE5QQ1RWQUNBTlQgPSBsb2coMStQQ1RWQUNBTlQpLAogICAgTE5QQ1RTSU5HTEVTID0gbG9nKDErUENUU0lOR0xFUykKICApCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTksIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9Cmxvbmdlcl92ZXJzaW9uPC0gZGF0YSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IGMoIk1FREhWQUwiLCAiUENUQkFDSE1PUiIsICJOQkVMUE9WMTAwIiwgIlBDVFZBQ0FOVCIsICJQQ1RTSU5HTEVTIiksCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIlZhcmlhYmxlIiwKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlZhbHVlIikKCmdncGxvdChsb25nZXJfdmVyc2lvbixhZXMoeCA9IFZhbHVlKSkgKwogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gLi5jb3VudC4uKSwgZmlsbCA9ICJibGFjayIsIGFscGhhID0gMC43KSArICAKICBmYWNldF93cmFwKH5WYXJpYWJsZSwgc2NhbGVzID0gImZyZWUiLCBuY29sID0gMywgbGFiZWxsZXIgPSBhc19sYWJlbGxlcihjKAogICAgIk1FREhWQUwiID0gIk1lZGlhbiBIb3VzZSBWYWx1ZSIsCiAgICAiUENUQkFDSE1PUiIgPSAiJSB3aXRoIEJhY2hlbG9y4oCZcyBEZWdyZWVzIG9yIEhpZ2hlciIsCiAgICAiTkJFTFBPVjEwMCIgPSAiIyBIb3VzZWhvbGRzIExpdmluZyBpbiBQb3ZlcnR5IiwKICAgICJQQ1RWQUNBTlQiID0gIiUgb2YgVmFjYW50IEhvdXNlcyIsCiAgICAiUENUU0lOR0xFUyIgPSAiJSBvZiBTaW5nbGUgSG91c2UgVW5pdHMiCiAgKSkpICsgIAogIGxhYnMoeCA9ICJWYWx1ZSIsIHkgPSAiQ291bnQiLCB0aXRsZSA9ICJIaXN0b2dyYW1zIG9mIERlcGVuZGVudCBhbmQgUHJlZGljdG9yIFZhcmlhYmxlcyIpICsKICB0aGVtZV9saWdodCgpICsgICAKICB0aGVtZShwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT02KSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT02KSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSkKYGBgCgoKYGBge3IsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTksIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMgaGlzdG9ncmFtcyBvZiB0aGUgdHJhbnNmb3JtZWQgdmFyaWFibGVzCmxvbmdlcl92ZXJzaW9uMiA8LSBkYXRhICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhMTk1FREhWQUwsIExOUENUQkFDSE1PUiAsTE5OQkVMUE9WMTAwLExOUENUVkFDQU5ULCBMTlBDVFNJTkdMRVMpLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJWYXJpYWJsZSIsCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJWYWx1ZSIpCgpnZ3Bsb3QobG9uZ2VyX3ZlcnNpb24yLGFlcyh4ID0gVmFsdWUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSAuLmNvdW50Li4pLCBmaWxsID0gInJlZCIsIGFscGhhID0gMC43KSArICAKICBmYWNldF93cmFwKH5WYXJpYWJsZSwgc2NhbGVzID0gImZyZWUiLCBuY29sID0gMywgbGFiZWxsZXIgPSBhc19sYWJlbGxlcihjKAogICAgIkxOTUVESFZBTCIgPSAiTG9nIE1lZGlhbiBIb3VzZSBWYWx1ZSIsCiAgICAiTE5QQ1RCQUNITU9SIiA9ICJMb2cgJSB3aXRoIEJhY2hlbG9y4oCZcyBEZWdyZWUiLAogICAgIkxOTkJFTFBPVjEwMCIgPSAiTG9nICMgSG91c2Vob2xkcyBpbiBQb3ZlcnR5IiwKICAgICJMTlBDVFZBQ0FOVCIgPSAiTG9nICUgVmFjYW50IEhvdXNlcyIsCiAgICAiTE5QQ1RTSU5HTEVTIiA9ICJMb2cgJSBTaW5nbGUgSG91c2UgVW5pdHMiCiAgKSkpICsgIAogIGxhYnMoeCA9ICJWYWx1ZSIsIHkgPSAiQ291bnQiLCB0aXRsZSA9ICJIaXN0b2dyYW1zIG9mIERlcGVuZGVudCBhbmQgbG9nIHRyYW5zZm9ybWVkIFByZWRpY3RvciBWYXJpYWJsZXMiKSArCiAgdGhlbWVfbGlnaHQoKSArICAgCiAgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9NiksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9NiksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCkpCmBgYAoKCgoKYGBge3IsZmlnLmhlaWdodD03LCBmaWcud2lkdGg9OSx3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpnZ3Bsb3Qoc2hhcGUpICsKICBnZW9tX3NmKGFlcyhmaWxsID0gTE5NRURIVkFMKSwgY29sb3IgPSAidHJhbnNwYXJlbnQiKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3JzID0gYygiI2ZmZjBmMyIsICIjYTQxMzNjIiksIAogICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiTE5NRURIVkFMIiwgCiAgICAgICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSAidHJhbnNwYXJlbnQiKSArIAogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksIGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbCA9IE5BLCBzaXplID0gMC44KSkgKwogIGxhYnModGl0bGUgPSAiTG9nIFRyYW5zZm9ybWVkIE1lZGlhbiBIb3VzZSBWYWx1ZSIpCmBgYAoKCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnNocGVfbG9uZ2VyPC0gc2hhcGUgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKCJQQ1RWQUNBTlQiLCAiUENUU0lOR0xFUyIsICJQQ1RCQUNITU9SIiwgIkxOTkJFTFBPViIpLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJWYXJpYWJsZSIsCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJWYWx1ZSIpCmN1c3RvbV90aXRsZXMgPC0gYygKICBQQ1RWQUNBTlQgICA9ICJQZXJjZW50IG9mIFZhY2FudCBIb3VzZXMiLAogIFBDVFNJTkdMRVMgID0gIlBlcmNlbnQgb2YgU2luZ2xlIEhvdXNlIFVuaXRzIiwKICBQQ1RCQUNITU9SICA9ICJQZXJjZW50IG9mIEJhY2hlbG9yJ3MgRGVncmVlIG9yIEhpZ2hlciIsCiAgTE5OQkVMUE9WICAgPSAiTG9nZ2VkIFRyYW5zZm9ybWVkIFBvdmVydHkgUmF0ZSIKKQoKCgpwbG90X2xpc3QgPC0gbGFwcGx5KHVuaXF1ZShzaHBlX2xvbmdlciRWYXJpYWJsZSksIGZ1bmN0aW9uKHZhcl9uYW1lKSB7CiAgZGF0YV9zdWJzZXQgPC0gc3Vic2V0KHNocGVfbG9uZ2VyLCBWYXJpYWJsZSA9PSB2YXJfbmFtZSkKICAKICBnZ3Bsb3QoZGF0YV9zdWJzZXQpICsKICAgIGdlb21fc2YoYWVzKGZpbGwgPSBWYWx1ZSksIGNvbG9yID0gInRyYW5zcGFyZW50IikgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudG4oCiAgICAgIGNvbG9ycyA9IGMoIiNmZmYwZjMiLCAiI2E0MTMzYyIpLAogICAgICBuYW1lID0gdmFyX25hbWUsCiAgICAgIG5hLnZhbHVlID0gInRyYW5zcGFyZW50IgogICAgKSArCiAgICBsYWJzKHRpdGxlID0gY3VzdG9tX3RpdGxlc1tbdmFyX25hbWVdXSkgKwogICAgdGhlbWUoCiAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSwKICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC4zLCAiY20iKSwKICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSwgZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUsIGZhY2UgPSAiYm9sZCIpLAogICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsID0gTkEsIHNpemUgPSAwLjgpCiAgICApCn0pCgojIENvbWJpbmUgdGhlIHBsb3RzIGludG8gYSBncmlkICgyIGNvbHVtbnMgYnkgMiByb3dzKQpjb21iaW5lZF9wbG90IDwtIChwbG90X2xpc3RbWzFdXSArIHBsb3RfbGlzdFtbMl1dKSAvCiAgICAgICAgICAgICAgICAgKHBsb3RfbGlzdFtbM11dICsgcGxvdF9saXN0W1s0XV0pCgpjb21iaW5lZF9wbG90CmBgYAoKCmBgYHtyIHJlZ3Jlc3Npb259CmZpdCA8LSBsbShMTk1FREhWQUwgfiBQQ1RWQUNBTlQgKyBQQ1RTSU5HTEVTICsgUENUQkFDSE1PUiArIExOTkJFTFBPVjEwMCwgZGF0YT1kYXRhKQpzdW1tYXJ5KGZpdCkKYGBgCgpgYGB7cn0KYW5vdmFfdGFibGUgPC0gYW5vdmEoZml0KQphbm92YV90YWJsZQpgYGAKCgpgYGB7cn0KZml0dGVkX3ZhbHVlcyA8LSBmaXR0ZWQoZml0KQpyZXNpZHVhbHNfdmFsdWVzIDwtIHJlc2lkdWFscyhmaXQpCnN0YW5kYXJkaXplZF9yZXNpZHVhbHMgPC0gcnN0YW5kYXJkKGZpdCkKCmRhdGEgPC0gZGF0YSAlPiUKICBtdXRhdGUoCiAgICBGaXR0ZWQgPSBmaXR0ZWRfdmFsdWVzLAogICAgUmVzaWR1YWxzID0gcmVzaWR1YWxzX3ZhbHVlcywKICAgIFN0YW5kYXJkaXplZF9SZXNpZHVhbHMgPSBzdGFuZGFyZGl6ZWRfcmVzaWR1YWxzKQpgYGAKCgoKYGBge3J9CmdncGxvdChkYXRhLCBhZXMoeCA9IEZpdHRlZCwgeSA9IFN0YW5kYXJkaXplZF9SZXNpZHVhbHMpKSArCiAgZ2VvbV9wb2ludChjb2xvciA9ICJibGFjayIsIHNpemU9IDAuNCkgKyAgICAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArICAKICBsYWJzKAogICAgdGl0bGUgPSAiU2NhdHRlciBQbG90IG9mIFN0YW5kYXJkaXplZCBSZXNpZHVhbHMgdnMgRml0dGVkIFZhbHVlcyIsCiAgICB4ID0gIlByZWRpY3RlZCBWYWx1ZXMiLAogICAgeSA9ICJTdGFuZGFyZGl6ZWQgUmVzaWR1YWxzIgogICkgKwogIHRoZW1lX21pbmltYWwoKSArICAgCiAgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9NiksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9NiksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCkpCmBgYAoKYGBge3J9CmdncGxvdChkYXRhLCBhZXMoeCA9IFN0YW5kYXJkaXplZF9SZXNpZHVhbHMpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMwLCBmaWxsID0gImJsYWNrIikgKwogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtIG9mIFN0YW5kYXJkaXplZCBSZXNpZHVhbHMiLCAKICAgICAgIHggPSAiU3RhbmRhcmRpemVkIFJlc2lkdWFscyIsIAogICAgICAgeSA9ICJGcmVxdWVuY3kiKSArCiAgdGhlbWVfbWluaW1hbCgpICsgICAKICB0aGVtZShwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT02KSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT02KSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSkKYGBgCgoKCmBgYHtyIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTksIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmxvbmdlcjwtZGF0YSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IGMoIlBDVEJBQ0hNT1IiLCAiTE5OQkVMUE9WMTAwIiwgIlBDVFZBQ0FOVCIsICJQQ1RTSU5HTEVTIiksCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIlZhcmlhYmxlIiwKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlZhbHVlIikKCmdncGxvdChsb25nZXIsYWVzKHggPSBWYWx1ZSwgeSA9IExOTUVESFZBTCkpICsKICBnZW9tX3BvaW50KGNvbG9yID0gImJsYWNrIiwgc2l6ZT0gMC40KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSAicmVkIiwgc2UgPSBGQUxTRSkgKyAKICBmYWNldF93cmFwKH4gVmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlIiwgbGFiZWxsZXIgPSBhc19sYWJlbGxlcihjKAogICAgIlBDVEJBQ0hNT1IiID0gIiUgd2l0aCBCYWNoZWxvcuKAmXMgRGVncmVlcyBvciBIaWdoZXIiLAogICAgIkxOTkJFTFBPVjEwMCIgPSAiTG9nZ2VkIEhvdXNlaG9sZHMgTGl2aW5nIGluIFBvdmVydHkiLAogICAgIlBDVFZBQ0FOVCIgPSAiJSBvZiBWYWNhbnQgSG91c2VzIiwKICAgICJQQ1RTSU5HTEVTIiA9ICIlIG9mIFNpbmdsZSBIb3VzZSBVbml0cyIKICApKSkgICsKICB0aGVtZV9saWdodCgpICsgICAKICB0aGVtZShwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT02KSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT02KSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSkgKwogIGxhYnModGl0bGUgPSAiU2NhdHRlciBQbG90cyBvZiBEZXBlbmRlbnQgVmFyaWFibGUgdnMuIFByZWRpY3RvcnMiLCAKICAgICAgIHggPSAiUHJlZGljdG9yIFZhbHVlIiwgCiAgICAgICB5ID0gIkxvZyBvZiBNZWRpYW4gSG91c2UgVmFsdWUiKQpgYGAKCgoKYGBge3IsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTksIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmpvaW48LSBkYXRhICU+JQogIGRwbHlyOjpzZWxlY3QoUE9MWV9JRCwgU3RhbmRhcmRpemVkX1Jlc2lkdWFscykKCnNoYXBlIDwtIHNoYXBlICU+JQogIGxlZnRfam9pbihqb2luLCBieSA9IGMoIlBPTFlfSUQiID0gIlBPTFlfSUQiKSkKCmdncGxvdChzaGFwZSkrCiAgZ2VvbV9zZihhZXMoZmlsbCA9IFN0YW5kYXJkaXplZF9SZXNpZHVhbHMpLCBjb2xvciA9ICJ0cmFuc3BhcmVudCIpICsKICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvcnMgPSBjKCIjZmZmMGYzIiwgIiNhNDEzM2MiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJTdGQgUmVzaWR1YWxzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSAidHJhbnNwYXJlbnQiKSArICAjIENob29zZSBhIGNvbG9yIHBhbGV0dGUsIGludmVydCBkaXJlY3Rpb24gaWYgbmVlZGVkCiAgbGFicyh0aXRsZSA9ICJDaG9yb3BsZXRoIE1hcCBvZiBTdGFuZGFyZGl6ZWQgUmVzaWR1YWxzIikgKwogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksIGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbCA9IE5BLCBzaXplID0gMC44KSkKCmBgYAoKCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KCmN1c3RvbV9sYWJlbHMgPC0gYygKICAiJSBvZiBJbmRpdmlkdWFscyB3aXRoIEJhY2hlbG9y4oCZcyBEZWdyZWVzIG9yIEhpZ2hlciIgPSAiUENUQkFDSE1PUiIsCiAgIiUgb2YgVmFjYW50IEhvdXNlcyIgPSAiUENUVkFDQU5UIiwKICAiJSBvZiBTaW5nbGUgSG91c2UgVW5pdHMiID0gIlBDVFNJTkdMRVMiLAogICIjIEhvdXNlaG9sZHMgTGl2aW5nIGluIFBvdmVydHkiID0gIkxOTkJFTFBPVjEwMCIKKQoKcHJlZGljdG9yX3ZhcnMgPC0gZGF0YVssIGMoIlBDVFZBQ0FOVCIsICJQQ1RTSU5HTEVTIiwgIlBDVEJBQ0hNT1IiLCAiTE5OQkVMUE9WMTAwIildCgpjb3JfbWF0cml4IDwtIGNvcihwcmVkaWN0b3JfdmFycywgdXNlID0gImNvbXBsZXRlLm9icyIsIG1ldGhvZCA9ICJwZWFyc29uIikKCnByaW50KGNvcl9tYXRyaXgpCnJvd25hbWVzKGNvcl9tYXRyaXgpIDwtIG5hbWVzKGN1c3RvbV9sYWJlbHMpCmNvbG5hbWVzKGNvcl9tYXRyaXgpIDwtIG5hbWVzKGN1c3RvbV9sYWJlbHMpCgoKZ2djb3JycGxvdChjb3JfbWF0cml4LCAKICAgICAgICAgICBtZXRob2QgPSAic3F1YXJlIiwgICAKICAgICAgICAgICB0eXBlID0gImxvd2VyIiwgICAgICAKICAgICAgICAgICBsYWIgPSBUUlVFLCAgICAgICAKICAgICAgICAgICBsYWJfc2l6ZSA9IDMsICAgICAgCiAgICAgICAgICAgY29sb3JzID0gYygiI2Q3MzAyNyIsICJ3aGl0ZSIsICIjMWE5ODUwIikpKwogICAgbGFicyh0aXRsZSA9ICJDb3JyZWxhdGlvbiBNYXRyaXggZm9yIGFsbCBQcmVkaWN0b3IgVmFyaWFibGVzIikgKwogICAgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSwgZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gNyksIAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKQpgYGAKCgpgYGB7cn0Kc3RlcHdpc2VfbW9kZWwgPC0gIHN0ZXBBSUMoZml0LCBkaXJlY3Rpb24gPSAiYm90aCIpCnN0ZXB3aXNlX21vZGVsJGFub3ZhCmBgYAoKYGBge3IgY3Jvc3MgdmFsaWRhdGlvbiwgbWVzc2FnZT1GQUxTRSwgd2FybmluZyA9IEZBTFNFfQoKbG0gPC0gIHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSA1KQoKY3ZsbV9tb2RlbCA8LSB0cmFpbihMTk1FREhWQUwgfiBQQ1RWQUNBTlQgKyBQQ1RTSU5HTEVTICsgUENUQkFDSE1PUiArIExOTkJFTFBPVjEwMCwgZGF0YT1kYXRhLCBtZXRob2QgPSAibG0iLCB0ckNvbnRyb2wgPSBsbSkKCnByaW50KGN2bG1fbW9kZWwpCgpgYGAKCgpgYGB7ciByZWR1Y2UgY3YgbW9kZWwsbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCmN2bG1fbW9kZWxfcmVkdWNlZCA9IHRyYWluKExOTUVESFZBTCB+IFBDVFZBQ0FOVCArIE1FREhISU5DLCBkYXRhID0gZGF0YSwgbWV0aG9kID0gImxtIiwgdHJDb250cm9sID0gbG0pCgpwcmludChjdmxtX21vZGVsX3JlZHVjZWQpCmBgYAo=